package com.xf.system.common.facade;

import com.xf.system.common.model.Dept;
import com.xf.system.enums.StatusEnum;
import com.xf.system.constants.BaseConst;
import com.xf.system.exception.BusinessException;
import com.xf.system.exception.Errors;
import com.xf.system.http.req.dept.DeptAddReqDTO;
import com.xf.system.http.req.dept.DeptListReqDTO;
import com.xf.system.http.req.dept.DeptUpdateReqDTO;
import com.xf.system.http.vo.dept.DeptTreeVO;
import com.xf.system.http.vo.dept.DeptVO;
import com.xf.system.common.model.BaseModel;
import com.xf.system.common.service.DeptService;
import com.xf.system.common.service.UserDeptService;
import com.xf.system.utils.StringUtil.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author xiefengf
 */
@Service
@Slf4j
public class DeptFacade {

    @Resource
    private DeptService deptService;

    @Resource
    private UserDeptService userDeptService;

    /**
     * 当前部门列表树型查询
     *
     * @param reqDto 分页信息+查询条件
     * @return 部门树集合
     */
    public DeptVO listDeptTree(DeptListReqDTO reqDto) {
        // 1、条件查询所有部门数据
        List<Dept> deptList = deptService.selectDeptList(reqDto);
        List<DeptTreeVO> deptTreeVoList = new ArrayList<>();
        // 2、根据顶级节点生成树形结构
        // 2.1 获取当前所有部门的主键id
        List<Long> idList = deptList.stream().map(BaseModel::getId).collect(Collectors.toList());
        // 2.2 按照父主键id分组
        Map<Long, List<Dept>> listMap = deptList.stream().collect(Collectors.groupingBy(Dept::getParentId));
        // 2.3、根据顶级节点生成树形结构 --- 没有查询条件就只有一棵树、有查询条件可能有多颗树
        if (reqDto.getDeptName() == null || "".equals(reqDto.getDeptName())){
            List<DeptTreeVO> treeNode1 = getDeptTree(listMap, BaseConst.TOP_LEVEL_NODE_ID);
            deptTreeVoList.addAll(treeNode1);
        }else {
            // 2.4 筛选并获取所有逻辑父节点的部门树 --- 此段代码为了条件查询数据而实现
            for (Dept dept : deptList) {
                // 如果主键集合中不包含当前节点的父节点，则代表当前节点就为逻辑父节点
                if (!idList.contains(dept.getParentId())) {
                    List<DeptTreeVO> treeNode2 = getDeptTree(listMap, dept.getParentId());
                    // 2.5 添加至集合中
                    deptTreeVoList.addAll(treeNode2);
                }
            }
        }
        // 3、封装数据
        DeptVO allVo = new DeptVO();
        allVo.setDeptTreeVoList(deptTreeVoList);
        return allVo;
    }

    /**
     * 通过部门id查询部门详情信息
     *
     * @param id 部门id
     * @return 部门详情信息
     */
    public DeptTreeVO getDeptById(Long id) {
        // 1、查询部门+校验部门是否存在+封装部门状态显示名称
        DeptTreeVO vo = deptService.selectDeptById(id);
        if (StringUtils.isNull(vo)) {
            throw new BusinessException(Errors.System.SYSTEM_01006);
        }
        // 2、设置父部门名称
        Dept deptByParentId = deptService.getById(vo.getParentId());
        if (StringUtils.isNotNull(deptByParentId)){
            vo.setParentName(deptByParentId.getDeptName());
        }else if (BaseConst.TOP_LEVEL_NODE_ID.equals(vo.getParentId())){
            vo.setParentName(BaseConst.TOP_LEVEL_NODE_NAME);
        }
        vo.setStatusName(StatusEnum.getByName(vo.getStatus()).getName());
        // 3、返回数据
        return vo;
    }

    /**
     * 新增部门信息
     *
     * @param reqDto 部门信息
     * @return 执行结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveDept(DeptAddReqDTO reqDto) {
        // 1、数据校验
        // 1.1 父部门已停用，不允许新增
        Dept parentDept = deptService.getById(reqDto.getParentId());
        if (parentDept != null && !BaseConst.TOP_LEVEL_NODE_ID.equals(reqDto.getParentId()) && StatusEnum.DISABLE.getValue().equals(parentDept.getStatus())) {
            // 父部门已停用，不允许新增
            throw new BusinessException(Errors.System.SYSTEM_01007);
        }
        // 1.2 当前父部门下部门名称唯一
        Dept deptByName = deptService.selectDeptByName(reqDto.getDeptName(), reqDto.getParentId());
        if (StringUtils.isNotNull(deptByName)) {
            // 部门名称已存在
            throw new BusinessException(Errors.System.SYSTEM_01008);
        }
        // 2、保存部门数据
        Dept dept = new Dept();
        BeanUtils.copyProperties(reqDto, dept);
        String ancestors = String.valueOf(BaseConst.TOP_LEVEL_NODE_ID);
        if (parentDept != null && !BaseConst.TOP_LEVEL_NODE_ID.equals(reqDto.getParentId())) {
            ancestors = parentDept.getAncestors() + BaseConst.SEPARATOR + dept.getParentId();
        }
        dept.setAncestors(ancestors);
        return deptService.save(dept);
    }

    /**
     * 更新部门信息
     *
     * @param reqDto 部门信息
     * @return 执行结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateDept(DeptUpdateReqDTO reqDto) {
        // 1、数据校验
        // 1.1 父部门已停用，不允许更新，且不是当前父部门（父部门有更新才校验）
        Dept parentDept = deptService.getById(reqDto.getParentId());
        if (!BaseConst.TOP_LEVEL_NODE_ID.equals(reqDto.getParentId()) && !reqDto.getParentId().equals(parentDept.getId()) && StatusEnum.DISABLE.getValue().equals(parentDept.getStatus())) {
            throw new BusinessException(Errors.System.SYSTEM_01007);
        }
        // 1.2 当前父部门下部门名称唯一，且不是当前部门
        Dept deptByName = deptService.selectDeptByName(reqDto.getDeptName(), reqDto.getParentId());
        if (StringUtils.isNotNull(deptByName) && !reqDto.getId().equals(deptByName.getId())) {
            throw new BusinessException(Errors.System.SYSTEM_01008);
        }
        // 1.3 上级部门不能是自己
        if (reqDto.getParentId().equals(reqDto.getId())) {
            throw new BusinessException(Errors.System.SYSTEM_01009);
        }
        // 1.4 当前部门下包含未停用的子部门，当前部门不能停用
        List<Dept> childrenDeptList = deptService.selectNormalChildrenDeptById(reqDto.getId());
        if (StatusEnum.DISABLE.getValue().equals(reqDto.getStatus()) && childrenDeptList.size() > 0) {
            throw new BusinessException(Errors.System.SYSTEM_01010);
        }
        // 2、根据id更新部门信息
        Dept dept = new Dept();
        BeanUtils.copyProperties(reqDto, dept);
        String parentAncestors = String.valueOf(BaseConst.TOP_LEVEL_NODE_ID);
        if (parentDept != null && !BaseConst.TOP_LEVEL_NODE_ID.equals(reqDto.getParentId())) {
            parentAncestors = parentDept.getAncestors() + BaseConst.SEPARATOR + dept.getParentId();
        }
        dept.setAncestors(parentAncestors);
        boolean update = deptService.updateById(dept);
        // 3、更新当前部门下属子部门的祖级列表字段/ancestors
        for (Dept childrenDept : childrenDeptList) {
            String ancestors = parentAncestors + BaseConst.SEPARATOR + childrenDept.getParentId();
            childrenDept.setAncestors(ancestors);
            deptService.updateById(childrenDept);
        }
        return update;
    }

    /**
     * 删除部门信息
     *
     * @param id 部门id
     * @return 执行结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteDept(Long id) {
        // 1、数据校验
        // 1.1 存在下级部门,不允许删除
        List<Dept> childrenDeptList = deptService.selectChildrenDeptById(id);
        if (!CollectionUtils.isEmpty(childrenDeptList)) {
            throw new BusinessException(Errors.System.SYSTEM_01011);
        }
        // 1.2 部门存在用户,不允许删除
        List<Long> userIdList = userDeptService.selectUserIdByDeptId(id);
        if (!CollectionUtils.isEmpty(userIdList)) {
            throw new BusinessException(Errors.System.SYSTEM_01012);
        }
        // 2、删除部门
        return deptService.removeById(id);
    }

    /**
     * 递归生成部门树
     *
     * @param listMap 部门列表数据
     * @param parentId 父部门id
     * @return 部门树
     */
    private List<DeptTreeVO> getDeptTree(Map<Long, List<Dept>> listMap, Long parentId) {
        // 初始化树结构列表
        List<DeptTreeVO> deptTreeVoList = new ArrayList<>();
        // 获取对应父主键id的列表
        List<Dept> deptList = listMap.get(parentId);
        // 为空则是叶子节点了
        if (CollectionUtils.isEmpty(deptList)){
            return deptTreeVoList;
        }
        // 循环遍历列表
        for (Dept dept : deptList) {
            // 封装部门数据到树结构节点对象
            DeptTreeVO vo = new DeptTreeVO();
            BeanUtils.copyProperties(dept, vo);
            vo.setStatusName(StatusEnum.getByName(dept.getStatus()).getName());
            // 递归调用，获取当前节点的所有子节点
            List<DeptTreeVO> deptTree = getDeptTree(listMap, dept.getId());
            vo.setChildren(deptTree);
            // 将当前节点添加到部门树结构列表
            deptTreeVoList.add(vo);
        }
        // 返回部门树结构列表
        return deptTreeVoList;
    }

    //    private List<DeptTreeVO> getDeptTree(List<Dept> deptList, Long parentId) {
//        // 初始化树结构列表
//        List<DeptTreeVO> deptTreeVoList = new ArrayList<>();
//        // 循环遍历列表
//        for (Dept dept : deptList) {
//            // 找到具有传入父节点ID的所有子节点
//            if (parentId.equals(dept.getParentId())) {
//                // 封装部门数据到树结构节点对象
//                DeptTreeVO vo = new DeptTreeVO();
//                BeanUtils.copyProperties(dept, vo);
//                vo.setStatusName(StatusEnum.getByName(dept.getStatus()).getName());
//                // 递归调用，获取当前节点的所有子节点
//                List<DeptTreeVO> deptTree = getDeptTree(deptList, dept.getId());
//                vo.setChildren(deptTree);
//                // 将当前节点添加到部门树结构列表
//                deptTreeVoList.add(vo);
//            }
//        }
//        // 返回部门树结构列表
//        return deptTreeVoList;
//    }


}
